package wxpay

import (
	"bgs/db"
	sqlc "bgs/db/sqlc"
	nsqpUtil "bgs/nsqp/util"
	afterSaleService "bgs/service/afterSale"
	brandService "bgs/service/brand"
	ledgerService "bgs/service/ledger"
	paymentService "bgs/service/payment"
	storeService "bgs/service/store"
	userService "bgs/service/user"
	"bgs/util"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/nsqio/go-nsq"
	"go.uber.org/zap"
)

// ProfitReturnNotification 分账回退变更消息
type ProfitReturnNotification struct {
	// 品牌id
	BrandID string `json:"brand_id"`
	// 商户分账单号
	OutOrderNo string `json:"out_order_no"`
	// 二级商户商户号
	SubMchID string `json:"sub_mch_id"`
	// 商户回退单号
	OutReturnNo string `json:"out_return_no"`
	// 回退商户ID
	ReturnMchID string `json:"return_mch_id"`
	// 回退金额
	Amount int64 `json:"amount"`
	// 回退描述
	Description string `json:"description"`
	// 微信回退单号
	WxReturnNo string `json:"wx_return_no"`
	// 微信分账单id
	WxOrderID string `json:"wx_order_id"`
	// 服务商AppID
	AppID string `json:"app_id"`
	// 服务商商户号
	MchID string `json:"mch_id"`
	// 回退结果
	Result string `json:"result"`
	// 失败原因 字段仅回退结果为FAIL时存在，枚举值：
	//		ACCOUNT_ABNORMAL：分账接收方账户异常
	//		TIME_OUT_CLOSED：超时关单
	FailReason string `json:"fail_reason"`
	// 完成时间 格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE
	FinishTime *time.Time `json:"finish_time"`
	// 消息时间戳
	Timestamp int64 `json:"timestamp"`
}

// ProfitReturnHandler wxpay profit return message handler
type ProfitReturnHandler struct {
	dao              *db.PgDao
	slog             *zap.SugaredLogger
	topic            string
	failover         *nsqpUtil.NsqFailover
	afterSaleService *afterSaleService.AfterSaleServiceImp
	brandService     *brandService.BrandServiceImp
	ledgerService    *ledgerService.LedgerServiceImp
	paymentService   *paymentService.PaymentServiceImp
	storeService     *storeService.StoreServiceImp
	userService      *userService.UserServiceImp
}

// NewProfitReturnHandler create a new handler for profit return
func NewProfitReturnHandler(
	pgdao *db.PgDao,
	slog *zap.SugaredLogger,
	topic string,
	failover *nsqpUtil.NsqFailover,
	afterSaleService *afterSaleService.AfterSaleServiceImp,
	brandService *brandService.BrandServiceImp,
	ledgerService *ledgerService.LedgerServiceImp,
	paymentService *paymentService.PaymentServiceImp,
	storeService *storeService.StoreServiceImp,
	userService *userService.UserServiceImp,
) *ProfitReturnHandler {
	return &ProfitReturnHandler{
		dao:              pgdao,
		slog:             slog.With("nsq_consumer_handler", "ProfitReturnHandler"),
		topic:            topic,
		failover:         failover,
		afterSaleService: afterSaleService,
		brandService:     brandService,
		ledgerService:    ledgerService,
		paymentService:   paymentService,
		storeService:     storeService,
		userService:      userService,
	}
}

// HandleMessage implements the Handler interface.
func (h *ProfitReturnHandler) HandleMessage(m *nsq.Message) error {
	if len(m.Body) == 0 {
		// Returning nil will automatically send a FIN command to NSQ to mark the message as processed.
		return nil
	}

	h.slog.Infof("message=>%s", string(m.Body))

	v := &ProfitReturnNotification{}
	if decodeErr := json.NewDecoder(bytes.NewReader(m.Body)).Decode(v); decodeErr != nil {
		return fmt.Errorf("无法解析消息[ProfitReturnNotification]: %s", decodeErr.Error())
	}

	reqID := strconv.FormatInt(v.Timestamp, 10)
	slog := h.slog.With("reqID", reqID)
	// 将来抽象到对应的业务组件
	ctx := context.WithValue(context.Background(), util.RequestID{}, reqID)
	var err error
	defer h.failover.BackupSubMsgWhenHandleFail(ctx, &err, nsqpUtil.BackupSubMsgParam{
		Ts:    v.Timestamp,
		Topic: h.topic,
		Mbody: m.Body,
	})

	wxLedgerReturnNo := v.WxReturnNo
	afterSaleSerialNumber := v.OutReturnNo
	occurred := time.Now()
	if v.FinishTime != nil {
		occurred = *v.FinishTime
	}

	isSuccess := v.Result == "SUCCESS"

	err = h.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {
		ledgerRollbackPtr, err := h.ledgerService.DoAfterWxpayProfitReturnFinish(ctx, q, ledgerService.DoAfterWxpayProfitReturnFinishParam{
			AfterSaleSerialNumber: afterSaleSerialNumber,
			Occurred:              occurred,
			Success:               isSuccess,
		})
		if err != nil {
			return
		}
		slog.Infof("a 本地分账回退完成 ok")

		if isSuccess {
			var order sqlc.Order

			order, err = q.GetOrderBySerialNumber(ctx, ledgerRollbackPtr.OrderSerialNumber)
			if err != nil {
				return
			}
			slog.Infof("b 找到分账回退的订单 ok")
			orderPtr := &order
			if ledgerRollbackPtr.DistributorCommission > 0 {
				slog.Infof("c 找到分账回退的分销订单需要记账")

				err = h.brandService.LoggingAccountForOrderDistributorCommissionRollback(ctx, q, ledgerRollbackPtr, orderPtr, wxLedgerReturnNo, occurred)
				if err != nil {
					return
				}
				slog.Infof("d1 商家分销订单佣金回退记账 ok")

				err = h.storeService.LoggingAccountForOrderDistributorCommissionProfitRollback(ctx, q, ledgerRollbackPtr, orderPtr, wxLedgerReturnNo, occurred)
				if err != nil {
					return
				}
				slog.Infof("d2 分销员分销订单佣金回退记账 ok")
			}

			if ledgerRollbackPtr.BonusAmount > 0 {

				if orderPtr.StoreID.Valid {
					slog.Infof("d 找到分账回退的门店推广订单需要记账")
					err = h.storeService.LoggingAccountForOrderBonusProfitRollback(ctx, q, ledgerRollbackPtr, orderPtr, wxLedgerReturnNo, occurred)
					if err != nil {
						return
					}
					slog.Infof("d1 门店订单奖励金回退记账 ok")

					err = h.userService.LoggingAccountForOrderBonusProfitRollbackOfStore(ctx, q, ledgerRollbackPtr, orderPtr, wxLedgerReturnNo, occurred)
					if err != nil {
						return
					}
					slog.Infof("d2 用户推广门店订单奖励金回退记账 ok")
				} else {
					slog.Infof("d 找到分账回退的品牌推广订单需要记账")
					err = h.brandService.LoggingAccountForOrderBonusProfitRollback(ctx, q, ledgerRollbackPtr, orderPtr, wxLedgerReturnNo, occurred)
					if err != nil {
						return
					}
					slog.Infof("d3 品牌订单奖励金回退记账 ok")

					err = h.userService.LoggingAccountForOrderBonusProfitRollbackOfBrand(ctx, q, ledgerRollbackPtr, orderPtr, wxLedgerReturnNo, occurred)
					if err != nil {
						return
					}
					slog.Infof("d4 用户推广品牌订单奖励金回退记账 ok")
				}
			}

			spokesmanUserID := order.SpokesmanUserID.Int64
			userAccount, lockErr := q.GetUserAccountByUserIDToLock(ctx, spokesmanUserID)
			if lockErr != nil {
				err = lockErr
				return
			}
			slog.Infof("e 获取带写入锁的用户账号 ok")

			if userAccount.AvailableAmount >= ledgerRollbackPtr.BonusAmount {
				_, err = h.userService.UpdateAccountAvailableAmountForOrderBonusWithLock(ctx, q, userService.UpdateAccountAvailableAmountForOrderBonusWithLockParam{
					UserAccountPtr:        &userAccount,
					OrderBonusAmountDelta: 0 - ledgerRollbackPtr.BonusAmount, // 回退金额在rollback是正,但对于用户账户来说需要是负,因此用0减
				})
				if err != nil {
					return
				}
				slog.Infof("f 可用余额>=分账回退金额 => 用户总账更新可用的奖励金 ok")
			} else {
				// 可用余额不足,从冻结金额走
				_, err = h.userService.UpdateAccountPendingAmountForOrderBonusWithLock(ctx, q, userService.UpdateAccountPendingAmountForOrderBonusWithLockParam{
					UserAccountPtr:        &userAccount,
					OrderBonusAmountDelta: 0 - ledgerRollbackPtr.BonusAmount, // 回退金额在rollback是正,但对于用户账户来说需要是负,因此用0减
				})
				if err != nil {
					return
				}
				slog.Infof("f 可用余额不足 => 用户总账更新冻结的奖励金 ok")
			}

			err = h.ledgerService.MarkLedgerRollbackAccounted(ctx, q, ledgerRollbackPtr)
			if err != nil {
				return
			}
			slog.Infof("g 标记分账回退已记账 ok")

			// 因为子商户预先分账,因此还是需要先分账回退然后再退款
			// var afterSale sqlc.AfterSale
			// afterSale, err = q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
			// if err != nil {
			// 	return
			// }
			// slog.Infof("h 分账回退成功,获取分账回退对应的售后单, 提交第三方交易平台退款 ok")

			// wxRefundID, err := h.paymentService.Refund(ctx, q, &afterSale)
			// if err != nil {
			// 	return err
			// }
			// slog.Infof("i 提交微信退款 ok")

			// err = h.afterSaleService.UpdateAfterSaleWxRefundID(ctx, q, afterSaleService.UpdateAfterSaleWxRefundIDParam{
			// 	AfterSaleID: afterSale.ID,
			// 	WxRefundID:  wxRefundID,
			// })
			// if err != nil {
			// 	return err
			// }
			// slog.Infof("j 微信退款单号更新到售后单 ok")

		} else {
			// 分账回退失败,必须标记售后单失败
			err = h.afterSaleService.CloseAfterSaleForRefundFail(ctx, q, afterSaleSerialNumber)
			if err != nil {
				return
			}
			slog.Infof("b 分账回退失败关闭售后单 ok")
		}
		return
	})

	if err != nil {
		return err
	}
	slog.Infof("消息[ProfitReturnNotification]处理成功")

	// Returning a non-nil error will automatically send a REQ command to NSQ to re-queue the message.
	return nil
}
